#include "stofac.h"
#include "bigmcallback.h"

#define DELTA -0.02

int  DZcreatlpinfo(CPXENVptr env, CPXLPptr lp, int ni, int ns, PROBINFOptr pinfo_p);
int  DZgetlpinfo(CPXENVptr env, CPXLPptr nodelp, PROBINFOptr pinfo_p, BIGMINFOptr minfo_p);
void DZdeletelpinfo(PROBINFOptr pinfo_p);
int  DZpopulatelpmod(CPXENVptr env, CPXLPptr lp, int ni, int ns, double eps);

int  DZchglpbds(CPXENVptr env, CPXLPptr lp, PROBINFOptr pp, BIGMINFOptr mp);

int main (int ac, char **av)
{
  /* First populate the problem */
  int status;
  int i,j,k;
  int flag = 0, niter=0, loopind = 1;
  double t0, t1, tbigm, tcopy;
  double objval, objvalcopy;
  int lastnode;
  
  /* Creat the data */
  int ni = atoi(av[1]);   //|I|
  int ns = atoi(av[2]);   //|W|
  double eps = 0.3;

  BIGMINFO minfo;

  PROBINFO pinfo, *pinfo_p;
  pinfo_p = &pinfo;

  /* Initialize CPLEX environment */
  CPXENVptr env = CPXopenCPLEX (&status);

  /* Creat one instance of the problem*/
  CPXLPptr lp   = CPXcreateprob (env, &status, "faclp");

  /* Read paramters from .prm file */
  status = CPXreadcopyparam(env, "./param/stofac.prm");

  /* Populate the model */
  status = DZpopulatelpmod(env, lp, ni, ns, eps);

  /* Clone the problem */
  CPXLPptr copy  = CPXcloneprob(env, lp, &status);

  /* Get lp data */
  status = DZcreatelpinfo(env, lp, ni, ns, pinfo_p);
  if ( status )  goto TERMINATE;

  /* Set minfo values */
  minfo.ni = ni;
  minfo.ns = ns;

  /* Solve copied problem */
  status = CPXgettime(env, &t0);
  status = CPXmipopt(env, copy);
  status = CPXgetobjval(env, copy, &objvalcopy);
  status = CPXgettime(env, &t1);
  tcopy = t1 - t0;

  /* Get all information from solving the original problem */
  status = DZgetlpinfo(env, copy, &pinfo, &minfo);

  /* Change bounds */
  status = DZchglpbds(env, lp, &pinfo, &minfo);

  /* Solve the original problem */
  status = CPXgettime(env, &t0);
  status = CPXmipopt(env, lp);      
  status = CPXgettime(env, &t1);
  tbigm = t1 - t0;
  status  = CPXgetobjval (env, lp, &objval);

  /* Output the message */
  printf("Objective by bigm  solver:    %f    Elapsed time: %f\n", objval, tbigm);
  printf("Objective by cplex solver:    %f    Elapsed time: %f\n", objvalcopy, tcopy);

  CPXwriteprob(env, lp, "stofac_lp.lp", NULL);
 TERMINATE:
  //  CPXfclose(logfile);
  CPXfreeprob (env, &lp);
  CPXfreeprob (env, &copy);
  CPXcloseCPLEX (&env);
  return 0;
}

int DZpopulatelpmod(CPXENVptr env, CPXLPptr lp, int ni, int ns, double eps)
{
  int i,j,k;
  int status;
  int nrows = ni * ns + 1;
  int ncols = ni + ns;
  char *rowname[nrows];
  char *colname[ncols];
  char truerowname[nrows][10];
  char truecolname[ncols][10];
  for(i = 0; i< nrows; i++) rowname[i] = truerowname[i];
  for(i = 0; i< ncols; i++) colname[i] = truecolname[i];
  char sense[nrows];
  char xctype[ncols];
  
  double *prbb = (double *)malloc(ns * sizeof(double));
  double *cost = (double *)malloc(ni * sizeof(double));
  double *obj  = (double *)malloc(ncols * sizeof(double));
  double *lb   =  (double *)malloc(ncols * sizeof(double));
  double *ub   =  (double *)malloc(ncols * sizeof(double));
  double *rhs  = (double *)malloc(nrows * sizeof(double));

  int zero = 0;  //should be zero
  int y_ind[ns];

  for(i = 0; i < ni; i++)   cost[i] = runif(5, 10);       //to be changed later on
  for(i = 1; i < nrows; i++) rhs[i] = runif(5,10);       //to be changed to random later on //used to be -runif(1,10)
  rhs[0] = eps;

  /* First create the columns */
  for(i = 0; i < ncols; i++)
    {
      if(i < ni)
        {
          sprintf(colname[i], "x_%d", i);
          lb[i] = 0.0;
	  ub[i] = CPX_INFBOUND;
	  //ub[i] = 10000.0;
          obj[i] = cost[i]; 
          xctype[i] = 'C';
        }
      else
        {
          sprintf(colname[i], "y_%d", i-ni);
          lb[i] = 0.0;
          ub[i] = 1.0;
          obj[i] = 0.0;  //should be zero here ,but we hack it.
          xctype[i] = 'B';
        }
    }
  /* Memo here, if the 7th argument is xctype then the problem is assumed to be MIP, here we explicitly set it to bu NULL so CPLEX will recognize it as a LP*/
  status = CPXnewcols(env, lp, ncols, obj, lb, ub, xctype, colname);
  if(status) return status;

  for(i = 0; i < ns; i++)      y_ind[i] = ni + i;
  for(i = 0; i < ns; i++)       prbb[i] = 1.0 / ns;  //equal probability

  /* Add the knapsack constraint */
  status = CPXaddrows(env, lp, 0, 1, ns, &eps, "L", &zero, y_ind, prbb, NULL, NULL);
  if(status) return status;

  int row_id;
  int xy_ind[2];
  double xy_val[2];
  //  printf("ni %i, ns %i\n", ni, ns);
  /* Add rest of the constraints */
  for(j = 0; j < ns; j++)
    { 
      for(i = 0; i < ni; i++)
        {
          row_id = 1 + j*ni + i;
          xy_ind[0] = i;
          xy_ind[1] = ni + j;
          xy_val[0] = 1; 
          xy_val[1] = rhs[row_id] + 1e-5; //to be changed
	    //= 0.1*rhs[row_id];
	    //rhs[row_id] + 1e-5; //to be changed
          // printf("add rhs[%i] : %f\n", row_id, rhs[row_id]);
          /* Add the demand constraint */
          status = CPXaddrows(env, lp, 0, 1, 2, &rhs[row_id], "G", &zero, xy_ind, xy_val, NULL, NULL);
        }
    }
 
  free(prbb);
  free(cost);
  free(obj);
  free(lb);
  free(ub);
  free(rhs);
  return 0;
}

int DZcreatelpinfo(CPXENVptr env, CPXLPptr lp, int ni, int ns, PROBINFOptr pinfo_p)
{
  pinfo_p->nbin = ni*ns;
  pinfo_p->ncon = ni;
  pinfo_p->nrows =  CPXgetnumrows(env, lp);
  pinfo_p->ncols =  CPXgetnumcols(env, lp);
  pinfo_p->nbigm =  pinfo_p->nrows -1 ;
  
  int nrows = pinfo_p->nrows;
  int ncols = pinfo_p->ncols;

  //  pinfo_p->nbin  =  ;
  //  pinfo_p->ncon  =  pinfo_p->ncols - pinfo_p->nbin;
  pinfo_p->a =     (double *)malloc(ncols * sizeof(double));
  pinfo_p->alb =   (double *)malloc(ncols * sizeof(double));
  pinfo_p->aub =   (double *)malloc(ncols * sizeof(double));
  pinfo_p->cstat =   (int *) malloc (ncols * sizeof(int));

  pinfo_p->rhs =   (double *)malloc(nrows * sizeof(double));
  pinfo_p->slack = (double *)malloc(nrows * sizeof(double));
  pinfo_p->rstat =   (int *) malloc (nrows * sizeof(int));

  /* Set pointer location for x and y */
  pinfo_p->x   = pinfo_p->a;
  pinfo_p->y   = pinfo_p->a  + pinfo_p->ncon;
  pinfo_p->xlb = pinfo_p->alb;
  pinfo_p->ylb = pinfo_p->alb + pinfo_p->ncon;
  pinfo_p->xub = pinfo_p->aub;
  pinfo_p->yub = pinfo_p->aub + pinfo_p->ncon;

  return 0;
}

int DZgetlpinfo(CPXENVptr env, CPXLPptr nodelp, PROBINFOptr pinfo_p, BIGMINFOptr minfo_p)
{
  int status;
  int i;
  /* Get solution infomation from node lp */
  status = CPXgetx (env, nodelp, pinfo_p->a, 0, pinfo_p->ncols-1);     //get value of x

  //  for(i = 0; i < pinfo_p->ncols; i++) printf("x[%i] is %f\n", i, (pinfo_p->a)[i]);

  status = CPXgetlb(env, nodelp, pinfo_p->alb, 0, pinfo_p->ncols-1);
  status = CPXgetub(env, nodelp, pinfo_p->aub, 0, pinfo_p->ncols-1);
  status = CPXgetslack(env, nodelp, pinfo_p->slack, 0, pinfo_p->nrows-1);
  status = CPXgetrhs(env, nodelp, pinfo_p->rhs, 0, pinfo_p->nrows-1);
  status = CPXgetbase (env,  nodelp, pinfo_p->cstat, pinfo_p->rstat);
  status = CPXgetobjval (env, nodelp, &(pinfo_p->obj));
  return 0;
}

void DZlpprobinfo(PROBINFOptr pinfo_p)
{
  free(pinfo_p->a);
  free(pinfo_p->rhs);
  free(pinfo_p->alb);
  free(pinfo_p->aub);
  free(pinfo_p->slack);
  free(pinfo_p->rstat);
  free(pinfo_p->cstat);
  return;
}

int DZgetlpbigmind(CPXENVptr env, CPXLPptr lp, PROBINFOptr pp, BIGMINFOptr mp)
{
  int i,j;
  int *zbigmrowind = NULL;
  int *zbigmcolind = NULL;
  int *zbigmchgind = NULL;
  double *zbigmval = NULL;
  int status = 0;
  int nrows = pp->nrows; 
  int ncols = pp->ncols;; 
  int nbin  = pp->nbin;
  int ncon  = pp->ncon; 
  int nbigm = pp->nbigm;

  zbigmrowind = (int *)malloc(nbigm * sizeof(int));
  zbigmcolind = (int *)malloc(nbigm * sizeof(int));
  zbigmchgind = (int *)malloc(nbigm * sizeof(int));
  //  zbigmval = (double *)malloc(nbigm * sizeof(double));

  printf("nrows %i, nbigm %i, nbin %i, ncon %i\n", nrows, nbigm, nbin, ncon);
  for(i = 0; i < nbigm; i++)
    {
      zbigmchgind[i] = 0;
      zbigmrowind[i] = i + 1;
      zbigmcolind[i] = ncon + (int)floor(1.0*i/ncon);
      /*
      status = CPXgetcoef(env, lp, 
			  zbigmrowind[i],
			  zbigmcolind[i],
			  &zbigmval[i]);
            printf("bigm index: row [%i], col[%i], val: %f\n ",  
	     zbigmrowind[i], zbigmcolind[i],
	     zbigmval[i]);
      */
    }

  /* Assign the allocated local memories to the original indices */

  mp->bigmrowind = zbigmrowind;
  mp->bigmcolind = zbigmcolind;
  mp->bigmchgind = zbigmchgind;

  //  CPXgetrowindex(env, lp, "resource1_scenario15 ", &colindex);
  //  CPXgetcolindex(env, lp, "y1", &rowindex);
  return 0;
}

int DZsetlpbigmval(CPXENVptr env, CPXLPptr lp, PROBINFOptr pp, BIGMINFOptr mp)
{
  double bigmval;
  int status = 0;
  int nrows = pp->nrows; 
  int ncols = pp->ncols;; 
  int nbin  = pp->nbin;
  int ncon  = pp->ncon; 
  int nbigm = pp->nbigm;
  int xind, yind;
  int *bigmrowind = mp->bigmrowind;
  int *bigmcolind = mp->bigmcolind;
  int i;
  for(i = 0; i < nbigm; i++)
    {
      status = CPXgetcoef(env, lp, bigmrowind[i], bigmcolind[i], &bigmval);

      //Set the bigval here
      bigmval = bigmval * 1.0;

      xind = i % ncon;
      yind = (int)floor(1.0*i/ncon);
      status = CPXchgcoef(env, lp, bigmrowind[i], bigmcolind[i], bigmval);
      //      printf("change bigm val here\n");
    }

  return 0;
}

int DZchglpbds(CPXENVptr env, CPXLPptr lp, PROBINFOptr pp, BIGMINFOptr mp)
{
  double bigmval;
  int xind;
  int yind;

  int status = 0;
  int nrows = pp->nrows; 
  int ncols = pp->ncols;; 
  int nbin  = pp->nbin;
  int ncon  = pp->ncon; 
  int nbigm = pp->nbigm;
  double tol = 0.00001;
  int i;
  double lb;

  for(i = 0; i < ncon; i++)
    {
      lb = (pp->x[i]) + DELTA;
      status = CPXchgbds (env, lp, 1, &i, "L", &lb);
    }
  return 0;
}


